/* 
 * Created on Nov 23, 2005
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 * Copyright @2005 the original author or authors.
 */
package com.googlecode.gjutil.util;

/**
 * <p>
 * Miscellaneous object utility methods.
 * </p>
 * <p>
 * This class is a partial copy of org.springmodules.util.Objects (Apache License, Version 2.0)
 * </p>
 * 
 * @author Alex Ruiz
 */
public abstract class Objects {

  private static final int INITIAL_HASH = 7;

  private static final int MULTIPLIER = 31;
  
  /**
   * Returns the same value as <code>{@link Boolean#hashCode()}</code>.
   * 
   * @param bool
   *          the given <code>boolean</code>.
   * @return the hash code for the given <code>boolean</code>.
   * @see Boolean#hashCode()
   */
  public static int hashCode(boolean bool) {
    return bool ? 1231 : 1237;
  }

  /**
   * Returns the same value as <code>{@link Double#hashCode()}</code>.
   * 
   * @param dbl
   *          the given <code>double</code>.
   * @return the hash code for the given <code>double</code>.
   * @see Double#hashCode()
   */
  public static int hashCode(double dbl) {
    long bits = Double.doubleToLongBits(dbl);
    return hashCode(bits);
  }

  /**
   * Returns the same value as <code>{@link Float#hashCode()}</code>.
   * 
   * @param flt
   *          the given <code>float</code>.
   * @return the hash code for the given <code>float</code>.
   * @see Float#hashCode()
   */
  public static int hashCode(float flt) {
    return Float.floatToIntBits(flt);
  }

  /**
   * Returns the same value as <code>{@link Long#hashCode()}</code>.
   * 
   * @param lng
   *          the given <code>long</code>.
   * @return the hash code for the given <code>long</code>.
   * @see Long#hashCode()
   */
  public static int hashCode(long lng) {
    return (int) (lng ^ (lng >>> 32));
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two <code>boolean</code> arrays <code>a</code> and <code>b</code>
   * such that <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * 
   * <p>
   * If <code>array</code> is <code>null</code>, this method returns 0.
   * </p>
   * 
   * @param array
   *          the array whose hash value to compute.
   * @return a content-based hash code for <code>array</code>.
   * @see #hashCode(boolean)
   */
  public static int nullSafeHashCode(boolean[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + hashCode(array[i]);
    }

    return hash;
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two <code>byte</code> arrays <code>a</code> and <code>b</code> such
   * that <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * 
   * <p>
   * If <code>array</code> is <code>null</code>, this method returns 0.
   * </p>
   * 
   * @param array
   *          the array whose hash value to compute.
   * @return a content-based hash code for <code>array</code>.
   */
  public static int nullSafeHashCode(byte[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + array[i];
    }

    return hash;
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two <code>char</code> arrays <code>a</code> and <code>b</code> such
   * that <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * 
   * <p>
   * If <code>array</code> is <code>null</code>, this method returns 0.
   * </p>
   * 
   * @param array
   *          the array whose hash value to compute.
   * @return a content-based hash code for <code>array</code>.
   */
  public static int nullSafeHashCode(char[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + array[i];
    }

    return hash;
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two <code>double</code> arrays <code>a</code> and <code>b</code> such
   * that <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * 
   * <p>
   * If <code>array</code> is <code>null</code>, this method returns 0.
   * </p>
   * 
   * @param array
   *          the array whose hash value to compute.
   * @return a content-based hash code for <code>array</code>.
   * @see #hashCode(double)
   */
  public static int nullSafeHashCode(double[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + hashCode(array[i]);
    }

    return hash;
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two <code>float</code> arrays <code>a</code> and <code>b</code> such
   * that <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * 
   * <p>
   * If <code>array</code> is <code>null</code>, this method returns 0.
   * </p>
   * 
   * @param array
   *          the array whose hash value to compute.
   * @return a content-based hash code for <code>array</code>.
   * @see #hashCode(float)
   */
  public static int nullSafeHashCode(float[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + hashCode(array[i]);
    }

    return hash;
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two <code>int</code> arrays <code>a</code> and <code>b</code> such
   * that <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * 
   * <p>
   * If <code>array</code> is <code>null</code>, this method returns 0.
   * </p>
   * 
   * @param array
   *          the array whose hash value to compute.
   * @return a content-based hash code for <code>array</code>.
   */
  public static int nullSafeHashCode(int[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + array[i];
    }

    return hash;
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two <code>long</code> arrays <code>a</code> and <code>b</code> such
   * that <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * 
   * <p>
   * If <code>array</code> is <code>null</code>, this method returns 0.
   * </p>
   * 
   * @param array
   *          the array whose hash value to compute.
   * @return a content-based hash code for <code>array</code>.
   * @see #hashCode(long)
   */
  public static int nullSafeHashCode(long[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + hashCode(array[i]);
    }

    return hash;
  }

  /**
   * <p>
   * Returns the value of <code>{@link Object#hashCode()}</code>. If the
   * object is an array, this method will delegate to any of the
   * <code>nullSafeHashCode</code> methods for arrays in this class.
   * </p>
   * 
   * <p>
   * If the object is <code>null</code>, this method returns 0.
   * </p>
   * 
   * @param obj
   *          the object whose hash value to compute.
   * @return the hash code of the given object.
   * @see #nullSafeHashCode(boolean[])
   * @see #nullSafeHashCode(byte[])
   * @see #nullSafeHashCode(char[])
   * @see #nullSafeHashCode(double[])
   * @see #nullSafeHashCode(float[])
   * @see #nullSafeHashCode(int[])
   * @see #nullSafeHashCode(long[])
   * @see #nullSafeHashCode(Object[])
   * @see #nullSafeHashCode(short[])
   */
  public static int nullSafeHashCode(Object obj) {
    if (obj == null)
      return 0;

    if (obj instanceof boolean[]) {
      return nullSafeHashCode((boolean[]) obj);
    }
    if (obj instanceof byte[]) {
      return nullSafeHashCode((byte[]) obj);
    }
    if (obj instanceof char[]) {
      return nullSafeHashCode((char[]) obj);
    }
    if (obj instanceof double[]) {
      return nullSafeHashCode((double[]) obj);
    }
    if (obj instanceof float[]) {
      return nullSafeHashCode((float[]) obj);
    }
    if (obj instanceof int[]) {
      return nullSafeHashCode((int[]) obj);
    }
    if (obj instanceof long[]) {
      return nullSafeHashCode((long[]) obj);
    }
    if (obj instanceof short[]) {
      return nullSafeHashCode((short[]) obj);
    }
    if (obj instanceof Object[]) {
      return nullSafeHashCode((Object[]) obj);
    }

    return obj.hashCode();
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two arrays <code>a</code> and <code>b</code> such that
   * <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * <p>
   * The value returned by this method is equal to the value that would be
   * returned by <code>Arrays.asList(a).hashCode()</code>, unless
   * <code>array</code> is <code>null</code>, in which case <code>0</code>
   * is returned.
   * </p>
   * 
   * @param array
   *          the array whose content-based hash code to compute.
   * @return a content-based hash code for <code>array</code>.
   */
  public static int nullSafeHashCode(Object[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + nullSafeHashCode(array[i]);
    }

    return hash;
  }

  /**
   * <p>
   * Returns a hash code based on the contents of the specified array. For any
   * two <code>short</code> arrays <code>a</code> and <code>b</code> such
   * that <code>Arrays.equals(a, b)</code>, it is also the case that
   * <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
   * </p>
   * 
   * <p>
   * If <code>array</code> is <code>null</code>, this method returns 0.
   * 
   * @param array
   *          the array whose hash value to compute
   * @return a content-based hash code for <code>array</code>
   */
  public static int nullSafeHashCode(short[] array) {
    if (array == null)
      return 0;

    int hash = INITIAL_HASH;
    int arraySize = array.length;
    for (int i = 0; i < arraySize; i++) {
      hash = MULTIPLIER * hash + array[i];
    }

    return hash;
  }

}
